using System.Collections.Generic;
using UnityEditor;
using UnityEditor.Rendering;
using UnityEngine;

namespace Mars.ShaderAnalysis
{
    public class ShaderAnalysisEditorWindow : EditorWindow
    {
        [MenuItem("Mars/Shader分析/Shader指令分析", false, 500)]
        public static void OpenWindow()
        {
            ShaderAnalysisEditorWindow editorWindow = GetWindow<ShaderAnalysisEditorWindow>(false, "Shader分析器", true);
            editorWindow.minSize = new Vector2(WINDOW_MIN_WIDTH, WINDOW_MIN_HEIGHT);
        }

        public static void OpenWindow(Shader shader)
        {
            ShaderAnalysisEditorWindow editorWindow = GetWindow<ShaderAnalysisEditorWindow>(false, "Shader分析器", true);
            editorWindow.minSize = new Vector2(WINDOW_MIN_WIDTH, WINDOW_MIN_HEIGHT);
            editorWindow.shader = shader;
        }
        
        
        private const float WINDOW_MIN_WIDTH = 700f;
        private const float WINDOW_MIN_HEIGHT = 700f;
        private const float SPACE_SIZE = 5f;
        private const float BUTTON_SIZE = 100f;

        private Vector2 scrollPosition = Vector2.zero;
        private ShaderType shaderType = ShaderType.Fragment;
        private ShaderCompilerPlatform shaderCompilerPlatform = ShaderCompilerPlatform.GLES3x;
        private BuildTarget buildTarget = BuildTarget.Android;
        private string keywordString = "";

        private Shader shader;
        private ShaderAnalysis analysis;

        public static void OpenWindow(string keywordString)
        {
            ShaderAnalysisEditorWindow editorWindow = GetWindow<ShaderAnalysisEditorWindow>(false, "Shader分析器", true);
            editorWindow.minSize = new Vector2(WINDOW_MIN_WIDTH, WINDOW_MIN_HEIGHT);
            editorWindow.ApplyKeywordString(keywordString);
            if (editorWindow.shader != null)
            {
                editorWindow.CompileVariantAndAnalysisPass();
            }
        }

        private void OnGUI()
        {
            if (shader != null && analysis == null)
            {
                AnalysisShader(shader);
            }

            using (new EditorGUILayoutUtility.VerticalScopeWithSpace(SPACE_SIZE + 3, SPACE_SIZE, SPACE_SIZE + 3, SPACE_SIZE))
            {
                using (new EditorGUILayout.VerticalScope("FrameBox"))
                {
                    using (new EditorGUILayout.HorizontalScope())
                    {
                        Shader newShader = EditorGUILayout.ObjectField("目标Shader", shader, typeof(Shader), false) as Shader;
                        if (shader != newShader)
                        {
                            shader = newShader;
                            if (shader == null)
                            {
                                analysis = null;
                            }
                            else
                            {
                                AnalysisShader(shader);
                            }
                        }
                    }

                    shaderType = (ShaderType)EditorGUILayout.EnumPopup("Shader阶段", shaderType);
                    shaderCompilerPlatform = (ShaderCompilerPlatform)EditorGUILayout.EnumPopup("编译平台", shaderCompilerPlatform);
                    buildTarget = (BuildTarget)EditorGUILayout.EnumPopup("构建平台", buildTarget);
                }

                using (new EditorGUILayout.VerticalScope("FrameBox"))
                {
                    if (analysis != null)
                    {
                        EditorGUILayout.LabelField(analysis.GetCombinationsString(), EditorStyles.boldLabel);
                    }

                    GUILayout.Space(SPACE_SIZE);
                    using (GUILayout.ScrollViewScope scrollViewScope = new GUILayout.ScrollViewScope(scrollPosition))
                    {
                        scrollPosition = scrollViewScope.scrollPosition;
                        OnShaderAnalysisGUI();
                    }

                    GUILayout.FlexibleSpace();
                }

                using (new EditorGUILayout.VerticalScope("FrameBox"))
                {
                    EditorGUILayout.LabelField(" 输入从FrameDebugger复制的Keywords");
                    using (new GUILayout.HorizontalScope())
                    {
                        GUILayout.Space(3f);
                        keywordString = EditorGUILayout.TextArea(keywordString,
                            GUILayout.Height(EditorGUIUtility.singleLineHeight * 2),
                            GUILayout.Width(position.width - EditorGUIUtility.singleLineHeight * 4 - 38f));
                        GUILayout.FlexibleSpace();
               
                        using (new EditorGUILayoutUtility.GUIColorScope(Color.green))
                        {
                            if (GUILayout.Button("编译分析", GUILayout.Width(EditorGUIUtility.singleLineHeight * 4), GUILayout.Height(EditorGUIUtility.singleLineHeight * 2)))
                            {
                                ApplyKeywordString(keywordString);
                                if (shader != null)
                                {
                                    CompileVariantAndAnalysisPass();
                                }
                            }
                        }

                        GUILayout.Space(3f);
                    }

                    GUILayout.Space(2f);
                }
            }
        }

        /// <summary>
        /// 分析GUI
        /// </summary>
        private void OnShaderAnalysisGUI()
        {
            if (analysis == null)
            {
                return;
            }

            //按SubShader显示
            for (int i = 0; i < analysis.subShaderAnalysisList.Count; i++)
            {
                EditorGUILayout.LabelField("SubShader " + i, EditorStyles.boldLabel);
                List<PassAnalysis> passAnalysisList = analysis.subShaderAnalysisList[i].passAnalysisList;
                //按Pass显示
                for (int j = 0; j < passAnalysisList.Count; j++)
                {
                    using (new EditorGUILayout.VerticalScope("FrameBox"))
                    {
                        PassAnalysis passAnalysis = passAnalysisList[j];
                        using (new EditorGUILayoutUtility.GUIColorScope(Color.yellow))
                        {
                            passAnalysis.isFoldOut = EditorGUILayout.Foldout(passAnalysis.isFoldOut,
                                " " + passAnalysis.passName + " (" + passAnalysis.lightMode + ") : Vertex [" + EditorGUILayoutUtility.LargeNumberToString(passAnalysis.GetVertexCombinesCount())
                                + "] + Fragment [" + EditorGUILayoutUtility.LargeNumberToString(passAnalysis.GetFragmentCombinesCount()) + "]", true);
                        }

                        if (passAnalysis.isFoldOut)
                        {
                            using (new EditorGUILayoutUtility.VerticalScopeWithSpace(15f, 0f, 3f, 0f))
                            {
                                OnShaderPassAnalysisGUI(passAnalysis, i, j);
                            }
                        }
                    }
                }
            }
        }

        private void OnShaderPassAnalysisGUI(PassAnalysis passAnalysis, int subShaderIndex, int passIndex)
        {
            //Keywords选择
            using (new EditorGUILayout.VerticalScope(GUI.skin.box))
            {
                using (new EditorGUILayout.HorizontalScope())
                {
                    //按Keyword显示
                    int openCount = 0;
                    List<ShaderKeywordsState> keywordsStateList = passAnalysis.keywordsStateList;
                    for (int k = 0; k < keywordsStateList.Count; k++)
                    {
                        ShaderKeywordsState shaderKeywordsState = keywordsStateList[k];
                        if (shaderKeywordsState.isOpen)
                        {
                            openCount++;
                        }
                    }

                    passAnalysis.isKeywordSelectFoldOut = EditorGUILayout.Foldout(passAnalysis.isKeywordSelectFoldOut, " 选择Keyword : " + openCount + " 组", true);
                    using (new EditorGUILayoutUtility.GUIColorScope(Color.cyan))
                    {
                        if (GUILayout.Button("查看编译代码", EditorStyles.miniButton, GUILayout.Width(BUTTON_SIZE)))
                        {
                            //编译Shader
                            string filePath = CompileVariantAnalysis.Compile(shader, subShaderIndex, passIndex,
                                passAnalysis.GetSelectedKeywordList().ToArray(), shaderType, shaderCompilerPlatform, buildTarget);
                            //打开文本文件
                            EditorUtility.OpenWithDefaultApp(filePath);
                        }
                    }
                }

                using (new EditorGUILayoutUtility.VerticalScopeWithSpace(20f, 0, 0f, 0))
                {
                    using (new EditorGUILayout.HorizontalScope())
                    {
                        //EditorGUILayout.LabelField("当前变体组合 : ", GUILayout.Width(80f));
                        using (new EditorGUILayoutUtility.GUIColorScope(Color.cyan))
                        {
                            //显示拼接字符串
                            string showKeywordString = "";
                            List<string> currentKeywords = passAnalysis.GetSelectedKeywordList();
                            for (int k = 0; k < currentKeywords.Count; k++)
                            {
                                showKeywordString += currentKeywords[k];
                                if (k != currentKeywords.Count - 1)
                                {
                                    showKeywordString += " ";
                                }
                            }

                            if (string.IsNullOrEmpty(showKeywordString))
                            {
                                showKeywordString = "_";
                            }

                            EditorGUILayout.TextArea(showKeywordString, EditorStyles.wordWrappedLabel);
                        }
                    }

                    if (passAnalysis.isKeywordSelectFoldOut)
                    {
                        List<ShaderKeywordsState> keywordsStateList = passAnalysis.keywordsStateList;
                        //按Keyword显示
                        for (int k = 0; k < keywordsStateList.Count; k++)
                        {
                            ShaderKeywordsState shaderKeywordsState = keywordsStateList[k];
                            if (shaderKeywordsState.isOpen)
                            {
                                using (new EditorGUILayout.HorizontalScope())
                                {
                                    EditorGUILayout.LabelField((k + 1).ToString("00") + ". ", GUILayout.Width(22f));
                                    EditorGUILayout.TextArea(shaderKeywordsState.keywordString, EditorStyles.wordWrappedLabel);
                                }

                                using (new EditorGUILayoutUtility.HorizontalScopeWithSpace(28f, 0, 0f, 0))
                                {
                                    List<string> keywords = shaderKeywordsState.keywords;
                                    for (int l = 0; l < keywords.Count; l++)
                                    {
                                        string keyword = keywords[l];
                                        using (new EditorGUILayoutUtility.GUIColorScope(shaderKeywordsState.selectKeywordIndex == l ? Color.green : GUI.color))
                                        {
                                            string showString = string.IsNullOrEmpty(keyword) ? "  _  " : keyword;
                                            if (GUILayout.Button(showString, EditorStyles.miniButton, GUILayout.Width(showString.Length * 10f + 2 * EditorGUILayoutUtility.controllerLeftRightSpaceWidth)))
                                            {
                                                shaderKeywordsState.selectKeywordIndex = l;
                                                GUIUtility.keyboardControl = 0;
                                            }
                                        }
                                    }
                                }
                            }
                        }

                        GUILayout.Space(3f);
                    }
                }
            }

            //编译分析结果
            using (new EditorGUILayout.VerticalScope(GUI.skin.box))
            {
                passAnalysis.isAnalysisFoldOut = EditorGUILayout.Foldout(passAnalysis.isAnalysisFoldOut, " 指令数分析结果", true);
                if (passAnalysis.isAnalysisFoldOut)
                {
                    using (new EditorGUILayoutUtility.HorizontalScopeWithSpace(15f, 0, 3f, 10f))
                    {
                        passAnalysis.vertexCompileVariantAnalysis.OnGUI();
                        passAnalysis.frameCompileVariantAnalysis.OnGUI();
                    }
                }
            }
        }

        /// <summary>
        /// 分析Shader
        /// </summary>
        private void AnalysisShader(Shader targetShader)
        {
            if (targetShader == null)
            {
                return;
            }

            shader = targetShader;
            if (analysis == null)
            {
                analysis = new ShaderAnalysis();
            }

            //设置Shader
            analysis.SetShader(shader);
            //解析Shader
            analysis.Execute();

            //若keywordString不为空，则需要解析并设置相应变体keyword
            if (string.IsNullOrEmpty(keywordString))
            {
                return;
            }

            string s = keywordString;
            if (keywordString.Contains("SubShader #"))
            {
                s = s.Split('\n')[1];
            }

            HashSet<string> keywordHash = new HashSet<string>();
            string[] keywords = s.Split(' ');
            for (int i = 0; i < keywords.Length; i++)
            {
                keywordHash.Add(keywords[i]);
            }

            //设置选中的keyword
            for (int i = 0; i < analysis.subShaderAnalysisList.Count; i++)
            {
                List<PassAnalysis> passAnalysisList = analysis.subShaderAnalysisList[i].passAnalysisList;
                for (int j = 0; j < passAnalysisList.Count; j++)
                {
                    PassAnalysis passAnalysis = passAnalysisList[j];
                    List<ShaderKeywordsState> keywordsStateList = passAnalysis.keywordsStateList;
                    for (int k = 0; k < keywordsStateList.Count; k++)
                    {
                        ShaderKeywordsState shaderKeywordsState = keywordsStateList[k];
                        if (shaderKeywordsState.isOpen)
                        {
                            for (int l = 0; l < shaderKeywordsState.keywords.Count; l++)
                            {
                                if (keywordHash.Contains(shaderKeywordsState.keywords[l]))
                                {
                                    shaderKeywordsState.selectKeywordIndex = l;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 编译并分析Pass
        /// </summary>
        private void CompileVariantAndAnalysisPass()
        {
            if (shader != null && analysis == null)
            {
                AnalysisShader(shader);
            }

            if (analysis == null)
            {
                return;
            }

            if (shaderCompilerPlatform != ShaderCompilerPlatform.GLES3x || buildTarget != BuildTarget.Android)
            {
                if (EditorUtility.DisplayDialog("提示", "目前只提供Android下GLES3的指令分析，是否确定修改选项并进行分析？", "确定", "取消"))
                {
                    shaderCompilerPlatform = ShaderCompilerPlatform.GLES3x;
                    buildTarget = BuildTarget.Android;
                    analysis.CompileVariantAndAnalysisPass(shaderCompilerPlatform, buildTarget, true);
                }
            }
            else
            {
                analysis.CompileVariantAndAnalysisPass(shaderCompilerPlatform, buildTarget, true);
            }
        }

        /// <summary>
        /// 读取并应用keyword字符串
        /// </summary>
        private void ApplyKeywordString(string s)
        {
            if (string.IsNullOrEmpty(s))
            {
                return;
            }

            //获取Shader
            if (s.Contains("SubShader #"))
            {
                string shaderName = s.Split("\n")[0].Split(',')[0];
                Shader tempShader = Shader.Find(shaderName);
                if (tempShader != null)
                {
                    shader = tempShader;
                }
            }

            keywordString = s;
            AnalysisShader(shader);
        }
    }
}